using System;
using System.Text;

using System.Collections.Generic;
using System.Xml;

namespace Gigya.Socialize.SDK
{
    /// <summary>
    /// Wraps the server's response.
    /// If the request was sent with the format set to "xml", the getData() will return null and you should use getRawData() instead.
    /// We only parse response text into GSDictionary if request format is set "json" which is the default. 
    /// </summary>
    /// <remarks>Author: Tamir Korem. Updated by: Yaron Thurm</remarks>
    public class GSResponse
    {
        #region Private Members
        private int errorCode = 0;
        private string errorMessage = null;
        private string responseText = "";
        private string format;
        private string method;
        private StringBuilder traceLog;
        private GSDictionary data;
        private static Dictionary<int, string> errorMsgDic = new Dictionary<int, string>();
        #endregion


        #region Static Constructor
        static GSResponse() 
        {
            errorMsgDic.Add(500026, "No Internet Connection");
            errorMsgDic.Add(400002, "Required parameter is missing");
        }
        #endregion

                    
        #region Constructors
        public GSResponse(string method, string responseText)
        {
            this.method = method;
            this.responseText = responseText = responseText.Trim();

            if (!string.IsNullOrEmpty(responseText))
            {
                if (responseText.StartsWith("{")) // JSON format
                {
                    this.format = "json";
                    this.data = new GSDictionary(responseText);
                    int intObj = this.data.getInt("errorCode", 0);
                    if (intObj != 0)
                        this.errorCode = intObj;

                    string strObj = data.getString("errorMessage", "");
                    if (strObj != "")
                        this.errorMessage = strObj;
                }
                else // XML format
                {
                    this.format = "xml";
                    // using string to avoid dependency on parser
                    string errCodeStr = this.getStringBetween(responseText, "<errorCode>", "</errorCode>");
                    if (errCodeStr != null)
                    {
                        this.errorCode = int.Parse(errCodeStr);
                        this.errorMessage = this.getStringBetween(responseText, "<errorMessage>", "</errorMessage>");
                    }                    
                }
            }
        }
        public GSResponse(string method, string format, int errorCode)
        {
            this.errorCode = errorCode;
            this.errorMessage = this.getErrorMessage();
            this.format = format;
            this.method = method;
            this.populateClientResponseText();
        }
        public GSResponse(string method, string format, int errorCode, string errorMessage)  : this(method, format, errorCode)
        {
            this.errorMessage = errorMessage;
        }

        public GSResponse(StringBuilder traceLog, String method, string responseText) : this(method, responseText)
        {
            this.traceLog = traceLog;
        }
        public GSResponse(StringBuilder traceLog, string method, string format, int errorCode) : this(method, format, errorCode)
        {
            this.traceLog = traceLog;
        }               
        public GSResponse(StringBuilder traceLog, String method, String format, int errorCode, String errorMessage) : this(method, format, errorCode, errorMessage)
        {
            this.traceLog = traceLog;
        }                
        #endregion


        #region Public Methods

        /// <summary>
        /// Returns the textual description, associated with the input error code.
        /// </summary>
        /// <param name="code">the error code. For the complete list of server error codes, see
        /// "http://wiki.gigya.com/030_API_reference/030_Response_Codes_and_Errors".</param>
        /// <returns>the error message string</returns>
        public static string getErrorMessage(int code)
        {
            if (code == 0 || !errorMsgDic.ContainsKey(code))
                return "";
            else
            {
                string errMsg;
                errorMsgDic.TryGetValue(code, out errMsg);
                return errMsg;
            }
        }

        /// <summary>
        /// Returns the result code of the operation. Code '0' indicates success, any other number indicates failure.
        /// For the complete list of server error codes, see 
        /// "http://wiki.gigya.com/030_API_reference/030_Response_Codes_and_Errors". 
        /// </summary>
        /// <returns>the error code</returns>
        public int getErrorCode()
        {
            return this.errorCode;
        }

        /// <summary>
        /// Returns a short textual description of the response error, for logging purposes.
        /// </summary>
        /// <returns>the error message string</returns>
        public string getErrorMessage()
        {
            if (this.errorMessage != null)
                return this.errorMessage;
            else
                return GSResponse.getErrorMessage(this.errorCode);
        }

        /// <summary>
        /// Returns the raw response data. 
        /// The raw response data is in JSON format, by default. If the request was sent with the format parameter set to "xml",
        /// the raw response data will be in XML format.
        /// </summary>
        /// <returns>the raw response data</returns>
        public string getResponseText()
        {
            return this.responseText;
        }

        /// <summary>
        /// Returns the response data in GSDictionary. 
        /// Please refer to Gigya's REST API reference (http://wiki.gigya.com/030_API_reference/020_REST_API), 
        /// for a list of response data structure per method request.
        /// Note: If the request was sent with the format parameter set to "xml", the getData() will return 
        /// null and you should use getResponseText() method instead. 
        /// We only parse response text into GSDictionary if the request format is "json", which is the default.
        /// </summary>
        /// <returns>a GSDictionary containing the response data</returns>
        public GSDictionary getData()
        {
            return this.data;
        }

        /// <summary>
        /// Returns the trace log of the response.
        /// </summary>
        /// <returns></returns>
        public string getLog()
        {
            return this.traceLog.ToString();
        }

        public override string ToString()
        {
            StringBuilder sb = new StringBuilder();
            sb.Append("\terrorCode:");
            sb.Append(errorCode);
            sb.Append("\n\terrorMessage:");
            sb.Append(errorMessage);
            sb.Append("\n\tdata:");
            sb.Append(data);
            return sb.ToString();
        }

        #endregion


        #region Private helper methods
        private void populateClientResponseText()
        {
            if (this.format == "json")
            {
                responseText = "{errorCode:" + errorCode + ",errorMessage:\"" + errorMessage + "\"}";
            }
            else
            {
                responseText = String.Format(@"
                <?xml version=""1.0"" encoding=""utf-8""?>
                <{0}Response xmlns:xsi=""http://www.w3.org/2001/XMLSchema-instance"" xsi:schemaLocation=""urn:com:gigya:api http://socialize-api.gigya.com/schema"" xmlns=""urn:com:gigya:api"">
                    <errorCode>{1}</errorCode>
                    <errorMessage>{2}</errorMessage>
                </{0}Response>"
                    , this.method,
                    this.errorCode,
                    this.errorMessage
                    );
            }
        }
        private string getStringBetween(string source, string prefix, string suffix)
        {
            if (source == null || source.Length == 0) return null;

            int prefixStart = source.IndexOf(prefix);
            int suffixStart = source.IndexOf(suffix);

            if (prefixStart == -1 || suffixStart == -1) return null;

            string ret = source.Substring(prefixStart + prefix.Length, suffixStart - (prefixStart + prefix.Length));
            return ret;
        }
        #endregion
    }
}
